/*
 *
 *  usb_ep0.c - S3C2410 USB controller driver.
 *              Endpoint zero management
 *
 *  Seungbum Lim <shawn@mizi.com>
 */

#include <arch.h>
#include "util.h"
#include "s3c2410_usb.h"  /* public interface */
#include "usb_ctrl.h"     /* private stuff */


#define USB_EP0_DBG 1
#if USB_EP0_DBG
#define DBGOUT(x) x
#else
#define DBGOUT(x)
#endif

//#define VERBOSITY 1

enum { false = 0, true = 1 };
#ifndef MIN
#define MIN( a, b ) ((a)<(b)?(a):(b))
#endif

#if 1 && !defined( ASSERT )
#  define ASSERT(expr) \
          if(!(expr)) { \
          printk( "Assertion failed! %s,%s,%s,line=%d\n",\
          #expr,__FILE__,__FUNCTION__,__LINE__); \
          }
#else
#  define ASSERT(expr)
#endif

extern int usb_connected;
unsigned int ep0_state; // sec like, shawn

__u8 set_configuration;
__u8 set_interface;
__u8 device_status;
__u8 ep0_status;
__u8 ep_bulk_in_status;
__u8 ep_bulk_out_status;
unsigned int control_complete;


/*================================================
 * USB Protocol Stuff
 */

/* Request Codes, standard request spec 1.1 */
enum { GET_STATUS=0,         CLEAR_FEATURE=1,     SET_FEATURE=3,
       SET_ADDRESS=5,        GET_DESCRIPTOR=6,      SET_DESCRIPTOR=7,
       GET_CONFIGURATION=8,  SET_CONFIGURATION=9, GET_INTERFACE=10,
       SET_INTERFACE=11 };


/* USB Device Requests */
typedef struct
{
    __u8 bmRequestType;
    __u8 bRequest;
    __u16 wValue;
    __u16 wIndex;
    __u16 wLength;
} usb_dev_request_t;

static int read_fifo( usb_dev_request_t * p );
static void standard_dev_req(usb_dev_request_t req);
static void set_feature(usb_dev_request_t req);
static void set_acm_config(void);
static void clear_feature(usb_dev_request_t req);
static void set_descriptor(void);
static void get_descriptor(usb_dev_request_t req);
static void ep0_transmit(void);
static void ep0_receive(void);
/***************************************************************************
Inline Helpers
***************************************************************************/

/* Data extraction from usb_request_t fields */
enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 };
static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); }

/* Standard Feature Selectors, shawn */
enum { fEndpoint_Halt = 0, fDevice_Remote_Wakeup = 1 };

static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); }
inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); } // ? [5,6] , shawn


/* print string descriptor */
static inline void psdesc( string_desc_t * p )
{
     int i;
     int nchars = ( p->bLength - 2 ) / sizeof( __u16 );
    // printk( "'" );
     for( i = 0 ; i < nchars ; i++ ) {
    //      printk( "%c", (char) p->bString[i] );
     }
    // printk( "'\n" );
}


#if VERBOSITY
/* "pcs" == "print control status" */
static inline void pcs( void )
{
    unsigned long backup;
    __u32 foo ;

    backup = rINDEX_REG;
    rINDEX_REG = UD_INDEX_EP0;
    foo = rIN_CSR1_REG;
    //printk( "%8.8X\n", foo);
    rINDEX_REG = backup;
}
static inline void preq( usb_dev_request_t * pReq )
{
     static char * tnames[] = { "dev", "intf", "ep", "oth" };
     static char * rnames[] = { "std", "class", "vendor", "???" };
     char * psz;
     switch( pReq->bRequest ) {
     case GET_STATUS: psz = "get stat"; break;
     case CLEAR_FEATURE: psz = "clr feat"; break;
     case SET_FEATURE: psz = "set feat"; break;
     case SET_ADDRESS: psz = "set addr"; break;
     case GET_DESCRIPTOR: psz = "get desc"; break;
     case SET_DESCRIPTOR: psz = "set desc"; break;
     case GET_CONFIGURATION: psz = "get cfg"; break;
     case SET_CONFIGURATION: psz = "set cfg"; break;
     case GET_INTERFACE: psz = "get intf"; break;
     case SET_INTERFACE: psz = "set intf"; break;
     default: psz = "unknown"; break;
     }
    // printk( "- [%s: %s req to %s. dir=%s]\n", psz,
    //         rnames[ (pReq->bmRequestType >> 5) & 3 ],
    //         tnames[ pReq->bmRequestType & 3 ],
    //         ( pReq->bmRequestType & 0x80 ) ? "in" : "out" );
}

#else
#define pcs() (void)(0)
#define preq(x) (void)(0)
#endif


/***************************************************************************
Globals
***************************************************************************/
static const char pszMe[] = "usbep0: ";


/* global write struct to keep write
   ..state around across interrupts */
static struct {
        unsigned char *p;
        int bytes_left;
        unsigned int transfer_length;
        unsigned int transfered_data;
} wr;




/***************************************************************************
Public Interface
***************************************************************************/

/* reset received from HUB (or controller just went nuts and reset by itself!)
  so udc core has been reset, track this state here  */
void
ep0_reset(void)
{
     /* reset state machine */
     wr.p = NULL;
     wr.bytes_left = 0;
     wr.transfer_length = 0;
     wr.transfered_data = 0;

     usbd_info.address = 0;
     control_complete = 0;
}

/* handle interrupt for endpoint zero */

#define EP0_STATE_IDLE         0
#define EP0_STATE_TRANSFER     1
#define EP0_STATE_RECEIVER     2

#define clear_ep0_sst { \
    rINDEX_REG = UD_INDEX_EP0; \
    rIN_CSR1_REG = 0x00; /*EP0_CSR_SENTSTL*/ \
}
#define clear_ep0_se { \
    __u32 reg;\
        rINDEX_REG = UD_INDEX_EP0; \
    reg=rIN_CSR1_REG;\
    reg=( reg & ~0xC0) | EP0_CSR_SSE; \
    rIN_CSR1_REG = reg;\
}
#define clear_ep0_opr { \
    __u32 reg;\
        rINDEX_REG = UD_INDEX_EP0; \
    reg=rIN_CSR1_REG;\
    reg=( reg & ~0xC0) | EP0_CSR_SOPKTRDY; \
    rIN_CSR1_REG=reg;\
}
#define set_ep0_ipr { \
    __u32 reg;\
        rINDEX_REG = UD_INDEX_EP0; \
    reg=rIN_CSR1_REG;\
    reg= ( reg & ~0xC0) | EP0_CSR_IPKRDY; \
    rIN_CSR1_REG=reg;\
}
#define set_ep0_de { \
    __u32 reg;\
        rINDEX_REG = UD_INDEX_EP0; \
    reg = rIN_CSR1_REG\
    reg = EP0_CSR_DE; \
    rIN_CSR1_REG= reg;\
}
#define set_ep0_ss { \
    __u32 reg;\
        rINDEX_REG = UD_INDEX_EP0; \
    reg = rIN_CSR1_REG;\
    reg = EP0_CSR_SENDSTL; \
    rIN_CSR1_REG = reg;\
}
#define set_ep0_de_out { \
    __u32 reg;\
        rINDEX_REG = UD_INDEX_EP0; \
    reg= rIN_CSR1_REG;\
    reg = ( reg & ~0xC0) | (EP0_CSR_SOPKTRDY | EP0_CSR_DE); \
    rIN_CSR1_REG = reg;\
}
#define set_ep0_de_in { \
    __u32 reg;\
        rINDEX_REG = UD_INDEX_EP0; \
    reg = rIN_CSR1_REG;\
    reg = ( reg & ~0xC0) |(EP0_CSR_IPKRDY | EP0_CSR_DE); \
    rIN_CSR1_REG=reg;\
}
#if 1
#define clear_stall_ep4_out { \
    __u32 reg; \
    rINDEX_REG = UD_INDEX_EP4; \
    reg = rOUT_CSR1_REG; \
    rOUT_CSR1_REG = reg; \
}
#define clear_stall_ep3_out { \
    __u32 reg; \
    rINDEX_REG = UD_INDEX_EP3; \
    reg = rOUT_CSR1_REG; \
    rOUT_CSR1_REG = reg; \
}
#endif
#define clear_stall_ep1_out { \
        __u32 reg; \
        rINDEX_REG = UD_INDEX_EP1; \
        reg = rOUT_CSR1_REG; \
        rOUT_CSR1_REG = reg; \
}

#define clear_stall_ep2_out { \
        __u32 reg; \
        rINDEX_REG = UD_INDEX_EP2; \
        reg = rOUT_CSR1_REG; \
        rOUT_CSR1_REG = reg; \
}

void udelay(int time)
{
    int i = time * 1000;
    while (i) {
        i--;
    }
}

usb_cdc_line_coding g_bspLineCode = { GS_DEFAULT_DTE_RATE,
                                   GS_DEFAULT_CHAR_FORMAT,
                                   GS_DEFAULT_PARITY,
                                   GS_DEFAULT_DATA_BITS };


//config the endpoints for the acm configuration accord to descriptor
static void set_acm_config()
{
    DBGOUT(printk("in set_acm_config\n"));
    //config endpoint 1 as IN endpoint
    rINDEX_REG = UD_INDEX_EP1;
    rMAXP_REG = UD_MAXP_64; // 64 byte

    rINDEX_REG = UD_INDEX_EP1;
    DBGOUT(printk("rMAXP_REG %x\n", rMAXP_REG));

    rINDEX_REG = UD_INDEX_EP1;
    rIN_CSR1_REG = UD_ICSR1_FFLUSH | UD_ICSR1_CLRDT; // fifo flush, data toggle
    rINDEX_REG = UD_INDEX_EP1;
    rIN_CSR2_REG = UD_ICSR2_MODEIN | UD_ICSR2_DMAIEN; // input mode; dma interrupt disable

    //config endpoint 2 as OUT endpoint
    rINDEX_REG = UD_INDEX_EP2;
    rMAXP_REG = UD_MAXP_64; // 64 byte

    rINDEX_REG = UD_INDEX_EP2;
    DBGOUT(printk("rMAXP_REG %x\n", rMAXP_REG));

    rINDEX_REG = UD_INDEX_EP2;
    rIN_CSR1_REG = UD_ICSR1_CLRDT; // fifo flush, data toggle
    rINDEX_REG = UD_INDEX_EP2;
    rIN_CSR2_REG = UD_ICSR2_DMAIEN; // output mode, IN_PKT_RDY dis
    rINDEX_REG = UD_INDEX_EP2;
    rOUT_CSR1_REG = UD_OCSR1_FFLUSH | UD_OCSR1_CLRDT; // fifo flush
    rINDEX_REG = UD_INDEX_EP2;
    rOUT_CSR2_REG = UD_OCSR2_DMAIEN; // OUT_PKT_RDY interrupt disable

    //enable endpoints interrupt
    rEP_INT_EN_REG = UD_INTE_EP0;

}

void ep0_int_hndlr( void )
{
    DBGOUT(printk("in ep0_int_hndlr!\n"));
    usb_dev_request_t req;
    int request_type, n ;
    u8 cs_reg;
    unsigned char *p = 0;
    unsigned short wLen = 0, wTotLen;
    int bSupport = 1;

    rINDEX_REG = UD_INDEX_EP0;
    cs_reg = rEP0_CSR;
    DBGOUT(printk("rEP0_CSR 0x%x\n", cs_reg));

    if( cs_reg & EP0_CSR_SENTSTL) {
        printk("EP0_CSR_SENTSTL\n");
        clear_ep0_sst;
        ep0_state = EP0_STATE_IDLE;
        return; //
    }

    if( cs_reg & EP0_CSR_SE) {
        printk("EP0_CSR_SE\n");
        clear_ep0_se;
        ep0_state = EP0_STATE_IDLE;
        return; //
    }

    switch(ep0_state)
    {
        case EP0_STATE_IDLE :
            if(cs_reg & EP0_CSR_OPKRDY) {

                /* read setup request */
            n = read_fifo(&req);

                if ( n != sizeof(req)) {
                      printk("%ssettup begin : fifo READ ERROR wanted %d bytes got %d. Stalling out...\n", pszMe, sizeof(req), n);
                      set_ep0_ss;
                    return;
                }
#if VERBOSITY
                {
                    unsigned char * pdb = (unsigned char *)&req;
                    printk( "%2.2X %2.2X %2.2X %2.2X"
                           " %2.2X %2.2X %2.2X %2.2X ",
                        pdb[0], pdb[1], pdb[2], pdb[3],
                        pdb[4], pdb[5], pdb[6], pdb[7]);
                    preq(&req);
                }
#endif

                request_type = type_code_from_request(req.bmRequestType);
                DBGOUT(printk("request_type %d\n", request_type));
                if (request_type != 0) {
                    //vendor/class command
                    DBGOUT(printk("Vendor/Class Command !!\n"));
                    DBGOUT(printk("Vendor/Class Command !!\n"));

                    wLen = req.wLength;
                    switch( req.bRequest ) {
                            case USB_CDC_REQ_SET_LINE_CODING:
                              DBGOUT(printk("USB_CDC_REQ_SET_LINE_CODING\n"));
                                wr.p = (unsigned char*)&g_bspLineCode;
                                wr.transfer_length = 0;
                                bSupport = 0;
                                break;

                            case USB_CDC_REQ_GET_LINE_CODING:
                                DBGOUT(printk("USB_CDC_REQ_GET_LINE_CODING\n"));
                                wr.p = (unsigned char*)&g_bspLineCode;
                                wr.transfer_length = (unsigned short)(sizeof(usb_cdc_line_coding)<wLen?sizeof(usb_cdc_line_coding):wLen);
                                wr.transfered_data = 0;
                                ep0_state = EP0_STATE_TRANSFER;
                                break;

                            case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
                                DBGOUT(printk("USB_CDC_REQ_SET_CONTROL_LINE_STATE\n"));
                                wr.p  = 0;
                                wr.transfer_length = 0;
                                bSupport = 0;
                                if (req.wValue & 0x03) {
                                    DBGOUT(printk("\n$$$Congratulations!!!You can Transmit data by Usb Now!!!!!!!!!!!\n\n"));
                                    usb_connected = 1;

//                                    dump_u2d_reg();
                                }
                                else {
                                    DBGOUT(printk("Device is Closed now, wait for it open!\n"));
                                }
                                break;

                            default:
                                /* Unknown vendor/class request */
                                DBGOUT(printk("Unknown vendor/class request\n"));
                                p  = 0;
                                wLen = 0;
                                wTotLen = 0;
                                break;
                    }
                    if (0 == bSupport) {
                        rINDEX_REG = UD_INDEX_EP0;
                        DBGOUT(printk("line %d, bSupport == 0, rEP0_CSR %x\n", __LINE__, rEP0_CSR));
                        if (wr.p) {
                            rINDEX_REG = UD_INDEX_EP0;
                            rEP0_CSR = EP0_CSR_SOPKTRDY;
                            DBGOUT(printk("need to read out the data in ep0 fifo!\n"));
//                            printk("need to read out the data in ep0 fifo!\n");
//                            U2DCSR0 |= (U2DCSR0_OPC | U2DCSR0_SA);
//                            ostdelay(1000); //wait for the data phase of USB_CDC_REQ_SET_LINE_CODING to finish
                            read_fifo((usb_dev_request_t *)wr.p);
                            wr.p = 0;
                            wr.transfer_length = 0;
//                            wTotLen = 0;

//                            rINDEX_REG = UD_INDEX_EP0;
//                            rEP0_CSR = EP0_CSR_SOPKTRDY;
                        }

//                        else {
                            DBGOUT(printk("LINE %d, clear out pkt ready\n", __LINE__));
                            rINDEX_REG = UD_INDEX_EP0;
                            rEP0_CSR = EP0_CSR_SOPKTRDY | EP0_CSR_DE;
//                        }
//                            rINDEX_REG = UD_INDEX_EP0;
//                            rEP0_CSR = EP0_CSR_SOPKTRDY;

//                        U2DCSR0 |= (U2DCSR0_OPC | U2DCSR0_SA);
//                        U2DCSR0 |= U2DCSR0_IPR;
                    }
                    if( wr.transfer_length ) {
                        //first need to clear EP0_CSR_OPKRDY
                        DBGOUT(printk("clear out pkt ready\n"));
                        rINDEX_REG = UD_INDEX_EP0;
                        rEP0_CSR = EP0_CSR_SOPKTRDY;
                        DBGOUT(printk("now transmit wLen %d datas!\n", wr.transfer_length));
                        ep0_transmit();
//                        queue_and_start_write( p,
//                                req.wLength,
//                                wLen );
                    }
                    udelay(30000);
//                    goto sh_sb_end;
                }
                else { //standard command
                    DBGOUT(printk("a standard request!\n"));
                    standard_dev_req(req);
                }
            }
            break;
        case EP0_STATE_TRANSFER :
            ep0_transmit();
            break;
        case EP0_STATE_RECEIVER :
            ep0_receive();
            break;
     }
     DBGOUT(printk( "---\n" ));
//     pcs();
     DBGOUT(printk( "\\/\n\n" ));
}


/* working shawn */
static void standard_dev_req(usb_dev_request_t req)
{
    __u32 address;
    int n, e;

    n = request_target(req.bmRequestType);
    DBGOUT(printk("request target %d\n", n));
    switch( n )
    {
    /* device recipient */
    case kTargetDevice :
        DBGOUT(printk("kTargetDevice\n"));
        switch(req.bRequest)
        {
        case SET_FEATURE :
            printk("SET_FEATURE\n");
            set_feature(req); /* work?, shawn */
            break;

        case CLEAR_FEATURE :
            printk("CLEAR_FEATURE\n");
            clear_feature(req); /* work? shawn */
            break;
        case SET_ADDRESS :
            DBGOUT(printk("SET_ADDRESS\n"));
            address = (__u32)(req.wValue & 0x7F);
            rFUNC_ADDR_REG = (address | 0x80);
            usbd_info.address = address;
            usbctl_next_state_on_event( kEvAddress );
            set_ep0_de_out;
            ep0_state = EP0_STATE_IDLE;
//    printk( "%sI have been assigned address: %d\n", pszMe, address );
            break;
        case SET_DESCRIPTOR :
            printk("SET_DESCRIPTOR\n");
            set_descriptor();
            break;
        case SET_CONFIGURATION :
            DBGOUT(printk("SET_CONFIGURATION\n"));
            set_configuration = (__u8)req.wValue; /* low byte */
#if 0
            set_ep0_de_out;
#endif
            if(req.wValue == 0 ) {  //de-configuration
                if( usbctl_next_state_on_event( kEvDeConfig ) != kError )
                   printk("%sDe-Configuration\n", pszMe );
            }
            if(req.wValue == 1 ) { // configured ID 1,here is a errro mode
                   printk("ERROR! SHOULD NOT ARRIVE HERE!\n");
            }
            else if( req.wValue == 2 ) {  //acm config ID
                DBGOUT(printk("line %d\n", __LINE__));
//                if( usbctl_next_state_on_event( kEvConfig ) != kError ) {
                    DBGOUT(printk("line %d\n", __LINE__));
                    set_acm_config();
//                }
            }
            else{
                printk("%ssetup phase : Unknown [set configuration] data %d\n", pszMe, req.wValue );
            }
            set_ep0_de_out;
            break;
        case GET_STATUS :
            printk("GET_STATUS\n");
            clear_ep0_opr;
            rEP0_FIFO = device_status;
            rEP0_FIFO = 0x00;
            set_ep0_de_in;
            break;
        case GET_CONFIGURATION :
            printk("GET_CONFIGURATION\n");
            clear_ep0_opr;
            rEP0_FIFO = set_configuration;
            set_ep0_de_in;
            break;
        case GET_DESCRIPTOR :
            DBGOUT(printk("GET_DESCRIPTOR\n"));
            get_descriptor(req);
            break;
        default :
            printk("default\n");
            set_ep0_de_out;
        }
        break;

        /* Interface Recipient */
    case kTargetInterface :
        DBGOUT(printk("kTargetInterface\n"));
        switch ( req.bRequest )
        {
        case SET_INTERFACE :
            printk("SET_INTERFACE\n");
            set_interface = req.wValue;
            set_ep0_de_out;
            break;
        case GET_INTERFACE :
            printk("GET_INTERFACE\n");
            clear_ep0_opr;
            rEP0_FIFO = set_interface;
            set_ep0_de_in;
            break;
        }
        break;
    case kTargetEndpoint :
        printk("kTargetEndpoint\n");
        switch( req.bRequest )
        {
        case SET_FEATURE :
            printk("SET_FEATURE\n");
            set_feature(req);
            break;
        case CLEAR_FEATURE :
            printk("CLEAR_FEATURE\n");
            clear_feature(req);
            break;
        case GET_STATUS :
            printk("GET_STATUS\n");
            clear_ep0_opr;
            e = windex_to_ep_num( req.wIndex );
            if( e == 0 ) {
            rEP0_FIFO = ep0_status;
            rEP0_FIFO = 0x00;
            }else if( e == 2 ) {
            rEP0_FIFO = ep_bulk_in_status;
            rEP0_FIFO = 0x00;
            }else if( e == 1 ) {
            rEP0_FIFO = ep_bulk_out_status;
            rEP0_FIFO = 0x00;
            }

            set_ep0_de_in;

            break;
        case GET_DESCRIPTOR :
            DBGOUT(printk("GET_DESCRIPTORn"));
            get_descriptor(req);
            break;
        }
    }
}



static void get_descriptor(usb_dev_request_t req)
{
    string_desc_t * pString;

    desc_t * pDesc = s3c2410_usb_get_descriptor_ptr();

    int type = req.wValue >> 8;
    int idx  = req.wValue & 0xFF;
    int i, len;

    switch( type )
    {
        case USB_DESC_DEVICE :
            DBGOUT(printk("USB_DESC_DEVICE\n"));
//            clear_ep0_opr;
//            rINDEX_REG = UD_INDEX_EP0;
//            rIN_CSR1_REG |= 1<<3;
            wr.p = (unsigned char *)&pDesc->dev;
            DBGOUT(printk("\nget descriptor() DEV: req.wLengh=%d, pDesc->dev.bength=%d\n\n"\
                ,req.wLength, pDesc->dev.bLength));
            DBGOUT(printk("\nget descriptor() DEV: req.wLengh=%d, pDesc->dev.bength=%d\n", req.wLength, pDesc->dev.bLength));
            wr.transfer_length = MIN(req.wLength, pDesc->dev.bLength);
            wr.transfered_data = 0;
            ep0_state = EP0_STATE_TRANSFER;

            //need to clear EP0_CSR_OPKRDY
            DBGOUT(printk("clear out pkt ready\n"));
            rINDEX_REG = UD_INDEX_EP0;
            rEP0_CSR = EP0_CSR_SOPKTRDY;


//            rIN_CSR1_REG |= EP0_CSR_DE | EP0_CSR_IPKRDY;
//            rIN_CSR1_REG |=
//           udelay(10);

            ep0_transmit();
            break;
        case USB_DESC_CONFIG :
            DBGOUT(printk("USB_DESC_CONFIG\n"));

            clear_ep0_opr;
    //        printk("\nget descriptor() CONFIG: req.wLengh=%d, pDesc->dev.bength=%d\n\n"
    //            ,req.wLength, pDesc->dev.bLength);


            config_desc_t *cfg =(config_desc_t*) (pDesc->cdb);
            len=0;
            for( i=0; i<pDesc->dev.bNumConfigurations; i++) {
                len += __le16_to_cpu(cfg->wTotalLength);
                cfg = (config_desc_t*) ( (unsigned char*) cfg
                        + __le16_to_cpu(cfg->wTotalLength)) ;
            }
            DBGOUT(printk("\nget descriptor() DEV: req.wLengh=%d, pDesc->config.bLength=%d\n\n"\
                ,req.wLength, len));

            wr.p = pDesc->cdb;
            wr.transfer_length = MIN(req.wLength, len);
//            wr.transfer_length = len;
            ep0_state = EP0_STATE_TRANSFER;
            wr.transfered_data = 0;
            ep0_transmit();
            break;
        case USB_DESC_STRING :
            DBGOUT(printk("STRING USB_DESC_STRING\n"));
            pString = s3c2410_usb_get_string_descriptor( idx );
            if( pString ) {
            if (idx != 0) { // if not language index
                printk("%sReturn string %d: ",pszMe, idx);
                psdesc( pString );
            }
            clear_ep0_opr;
            wr.p = (unsigned char *)pString;
            wr.transfer_length = MIN(req.wLength, pString->bLength);
            DBGOUT(printk("\nget descriptor() String: req.wLengh=%d, pDesc->dev.bength=%d\n\n"\
                   ,req.wLength, pDesc->dev.bLength));
            ep0_state = EP0_STATE_TRANSFER;
            wr.transfered_data = 0;
            ep0_transmit();
            break;
            }

        default :
            clear_ep0_opr;
            set_ep0_de_in;
            break;
        }

}

static void ep0_transmit(void)
{
    int i, data_length;
    u8 reg_ep0_status;
    u8 reg_int_status;

    rINDEX_REG = UD_INDEX_EP0;
    reg_ep0_status = rEP0_CSR;


    DBGOUT(printk("EP0 want to send : %d byte, reg_ep0_status 0x%x\n",wr.transfer_length, reg_ep0_status));


    /* ep0 input fifo check */
    if( ( reg_ep0_status & EP0_CSR_IPKRDY) == 0 ) {
        data_length = wr.transfer_length - wr.transfered_data;
        DBGOUT(printk("line %d data_length %d\n", __LINE__, data_length));
        data_length = MIN(data_length, 8); /* EP0 MAX Packet size == 8 */


        /* need to check RESET, RESUME and SUSPEND Interrupts */
        reg_int_status = rUSB_INT_REG;
        printk("reg_int_status %x\n", reg_int_status);
        reg_int_status &= UD_USBINT_RESET | UD_USBINT_RESUM | UD_USBINT_SUSPND;
        if( reg_int_status == UD_USBINT_RESET) {// if RESET occures, just return
            printk("UD_USBINT_RESET\n");
            ep0_state = EP0_STATE_IDLE;
            return;
        }


        DBGOUT(printk(" SENDING... ["));
        for(i = 0; i < data_length; i++) {
            rEP0_FIFO = *wr.p;
            DBGOUT(printk("%2.2X ", *wr.p));
            wr.p++;
            wr.transfered_data++;

        }
        DBGOUT(printk("] \n"));
        DBGOUT(printk("EP0 transfered : %d bytes\n", wr.transfered_data));
        DBGOUT(printk("wr.transfered_data = %d, wr.transfer_length = %d\n",
                wr.transfered_data, wr.transfer_length));


        if( wr.transfered_data == wr.transfer_length) {
            reg_int_status = rUSB_INT_REG;
            reg_int_status &= UD_USBINT_RESET | UD_USBINT_RESUM | UD_USBINT_SUSPND;

            if(reg_int_status == UD_USBINT_RESET)
                return;
            if( (wr.transfer_length % 16) == 0 && control_complete == 0 ) {
                control_complete = 1;
                set_ep0_ipr;
            }
            else{
                control_complete = 0;
                set_ep0_de_in;
                ep0_state = EP0_STATE_IDLE;
            }
            return;
        }
        rINDEX_REG = UD_INDEX_EP0;
        rEP0_CSR = EP0_CSR_IPKRDY;
//        set_ep0_ipr;
    }
    DBGOUT(printk("sending"));
//    while (rEP0_CSR & EP0_CSR_IPKRDY) {
//        DBGOUT(printk("rEP0_CSR %x\n", rEP0_CSR));
//    }
}

static void set_feature(usb_dev_request_t req)
{
    int ep;

    switch( req.wValue )
    {
    case fEndpoint_Halt :
        ep = windex_to_ep_num( req.wIndex );
        if( ep == 0 ) {
        printk("%sset feature [endpoint halt] on control\n", pszMe);
        ep0_status = 0x001;
        set_ep0_ss;
        clear_ep0_opr;
        }
        else if( ep == 2 ) {
        printk("%set feature [endpoint halt] on xmitter\n",pszMe);
        ep_bulk_in_status = 0x0001;
        rINDEX_REG = UD_INDEX_EP2;
        rIN_CSR1_REG |= UD_ICSR1_CLRDT;
    //    ep2_stall();

        }
        else if( ep == 1 ) {
        printk("%set feature [endpoint halt] on receiver\n",pszMe);
        ep_bulk_out_status = 0x0001;

        rINDEX_REG = UD_INDEX_EP1;
        rOUT_CSR1_REG |= UD_OCSR1_SENDSTL;
    //    ep1_stall();
        }
        else {
        printk("%sUnsupported feature selector (%d) in set feature\n", pszMe, req.wValue);
        set_ep0_de_out;
        }
        break;
    case fDevice_Remote_Wakeup :
        device_status = 0x02;
        set_ep0_de_out;
        break;
    default :
        set_ep0_de_out;
        break;
    }
}

static void clear_feature(usb_dev_request_t req)
{
    int ep;

    switch( req.wValue )
    {
    case fEndpoint_Halt :
        ep = windex_to_ep_num( req.wIndex );
        if( ep == 0 )  {// ep0
            ep0_status = 0x0000;
        }
        else if( ep == 2 ) { // ep2  input
            ep_bulk_in_status = 0x0000;
              clear_stall_ep4_out; // ep4??
//            printk(__FUNCTION__"(): confused. - bushi\n");
        }
        else if( ep == 1 ) { // ep1 output
            ep_bulk_out_status = 0x0000;
            clear_stall_ep1_out; //??
        }
        else
            printk("%sUnsupported endpoint (%d)\n", pszMe, ep);

        set_ep0_de_out;
        break;
    case fDevice_Remote_Wakeup :
        device_status = 0x00;
        set_ep0_de_out;
        break;
    default :
        set_ep0_de_out;
        break;
    }
}

static void set_descriptor(void)
{
    set_ep0_de_out;
}

static void ep0_receive(void)
{

}
/*
 * read_fifo()
 * Read 1-8 bytes out of FIFO and put in request.
 * Called to do the initial read of setup requests
 * from the host. Return number of bytes read.
 *
 * Like write fifo above, this driver uses multiple
 * reads checked agains the count register with an
 * overall timeout.
 *
 */


static int read_fifo( usb_dev_request_t * request )
{
    int bytes_read = 0;
    int fifo_count = 0;
    int i, ep;
    unsigned char * pOut = (unsigned char*) request;

    ep = windex_to_ep_num( request->wIndex ) & 0;
//    printk(__FUNCTION__"(): ep = %d\n", ep);
    DBGOUT(printk("ep %x\n", ep));
    printk("ep %x\t", ep);
    switch (ep) {
        case 0:
            rINDEX_REG = UD_INDEX_EP0;
            break;
        case 2:
            rINDEX_REG = UD_INDEX_EP2;
            break;
        case 1:
            rINDEX_REG = UD_INDEX_EP1;
            break;
        default:
//            printk(__FUNCTION__"(): ???? ep = %d\n", ep);
            rINDEX_REG = UD_INDEX_EP0;
}

    fifo_count = (( rOUT_FIFO_CNT2_REG << 8) | rOUT_FIFO_CNT1_REG) & 0xffff;

    ASSERT( fifo_count <= 8 );
//    printk(__FUNCTION__ "(): fifo_count =%d ", fifo_count );
    DBGOUT(printk("fifo count %d\n", fifo_count));

    while( fifo_count-- ) {
         i = 0;
         do {
              *pOut = (unsigned char) rEP0_FIFO;
                udelay( 10 );
                DBGOUT(printk("0x%x\t", *pOut));
                i++;
         } while( ( (( rOUT_FIFO_CNT2_REG << 8) | rOUT_FIFO_CNT1_REG) & 0xffff ) != fifo_count && i < 10 );
         if ( i == 10 ) {
              printk( "%sread_fifo(): read failure\n", pszMe );
              usbd_info.stats.ep0_fifo_read_failures++;
         }
         pOut++;
         bytes_read++;
    }
    DBGOUT(printk("\nbytes_read %d\n", bytes_read));

    DBGOUT(printk( "bytes_read=%d\n", bytes_read ));
    usbd_info.stats.ep0_bytes_read++;

    return bytes_read;

}

/* end usb_ep0.c */
